#include "MatterBridge.h"

#include <platform/CHIPDeviceLayer.h>
#include <app/CommandHandler.h>
#include <lib/support/logging/CHIPLogging.h>
#include <fstream>
#include <app/reporting/reporting.h>
#include <thread>
#include <unistd.h>

#include "LightColor.h"
#include "Thermostat.h"
#include "WeatherSensor.h"
#include "OccupancySensor.h"
#include "LightSensor.h"
#include "Fan.h"


MatterBridge MatterBridge::sInstance;

MatterBridge::MatterBridge() = default;

MatterBridge::~MatterBridge() {
    delete mClient;

    for (auto &mDevice: mDevices) {
        if (mDevice != nullptr) {
            delete mDevice;
            mDevice = nullptr;
        }
    }
    for (const auto item: mNodes) {
        delete item;
    }
    mNodes.clear();
}

ChipError MatterBridge::LoadConfig(const std::string &filename) {
    std::ifstream inputFile(filename);
    if (!inputFile.is_open()) {
        ChipLogError(DeviceLayer, "failed to open file: %s", filename.c_str());
        return CHIP_ERROR_NOT_FOUND;
    }

    Json::Value config;
    Json::CharReaderBuilder readerBuilder;
    std::string errs;
    bool successful = parseFromStream(readerBuilder, inputFile, &config, &errs);
    inputFile.close();
    if (!successful) {
        ChipLogError(DeviceLayer, "failed to parse json: %s", errs.c_str());
        return CHIP_ERROR_NOT_FOUND;
    }

    mClient = new WebSocketClient(config["remote_url"].asString(), this);
    std::thread connectThread([this] {
        sleep(3);
        while (true) {
            auto err = this->mClient->Run();;
            ChipLogError(DeviceLayer, "websocket client ret =  %d", err);
            sleep(5);
        }
    });
    connectThread.detach();

    ChipLogProgress(DeviceLayer, "ok");
    LoadNodes(config["nodes"]);
    return CHIP_NO_ERROR;
}


void MatterBridge::OnMessageSend(DataBlock *block) {
    Json::Value root;
    root["method"] = "write.attribute";
    root["data"] = block->Encode();
    ChipLogDetail(DeviceLayer, "sendMessage <- %s", Json::FastWriter().write(root).c_str());
    mClient->SendMessage(root);
}

void MatterBridge::OnGetRemoteAttribute(Path *path) {
    Json::Value root;
    root["method"] = "read.attribute";
    root["data"] = path->Encode();
    ChipLogDetail(DeviceLayer, "sendMessage <- %s", Json::FastWriter().write(root).c_str());
    mClient->SendMessage(root);
}

void MatterBridge::OnStatusChanged(app::ConcreteAttributePath *path) {
    DeviceLayer::PlatformMgr().ScheduleWork([](intptr_t closure) {
        const auto p = reinterpret_cast<app::ConcreteAttributePath *>(closure);
        MatterReportingAttributeChangeCallback(*p);
        Platform::Delete(p);
    }, reinterpret_cast<intptr_t>(path));
}


void MatterBridge::OnWebSocketStateChanged(WebSocketClient::State state) {
    if (state == WebSocketClient::State::Established) {
        Path path(
            app::Clusters::BridgedDeviceBasicInformation::Id,
            app::Clusters::BridgedDeviceBasicInformation::Attributes::Reachable::Id);
        path.endpointId = kInvalidEndpointId;
        Json::Value root;
        root["method"] = "read.attribute";
        root["data"] = path.Encode();
        mClient->SendMessage(root);

        for (auto &device: mDevices) {
            if (device != nullptr) {
                device->OnReadAttributes();
            }
        }
    }
}

void MatterBridge::OnWebSocketMessageReceived(const Json::Value &root) {
    const auto method = root["method"].asString();
    if (method.empty()) {
        return;
    }

    const std::string encoding = Json::FastWriter().write(root["data"]);
    ChipLogDetail(DeviceLayer, "incoming: %s", encoding.c_str());

    if (method == "report.attribute") {
        const DataBlock block(root["data"]);
        if (const auto dev = GetDeviceByEndpoint(block.path.endpointId); dev != nullptr) {
            dev->OnReport(&block);
        }
    } else if (method == "read.attribute.response") {
        for (const auto & item : root["data"]) {
            if (const DataBlock block(item); block.status == 0) {
                ChipLogDetail(DeviceLayer, "status: %s", Json::FastWriter().write(item).c_str());
                if (const auto dev = GetDeviceByEndpoint(block.path.endpointId); dev != nullptr) {
                    dev->OnReport(&block);
                }
            }
        }
    }
}

int MatterBridge::LoadNodes(const Json::Value &initParams) {
    if (initParams.type() != Json::arrayValue) {
        return -1;
    }

    for (const auto &item: initParams) {
        const auto node = BasicInformation::Create(item);
        if (node == nullptr) {
            continue;
        }
        mNodes.push_back(node);
    }

    for (const auto &node: mNodes) {
        ChipLogDetail(DeviceLayer, "node: %s", node->ToString().c_str());
    }
    return 0;
}

int MatterBridge::AttachDevice(BridgedDevice *dev) {
    if (dev->GetParentEndpointId() == kInvalidEndpointId) {
        dev->SetParentEndpointId(mBridgeAnonymousEndpointId);
    }
    uint16_t index = GetNullptrIndex();
    if (index == CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) {
        ChipLogProgress(DeviceLayer, "Failed to add dynamic endpoint: No endpoints available!");
        return -1;
    }

    // 设备加载进去的时候可能会理解读取属性，因此将其先放到 gDevices 中
    mDevices[index] = dev;
    ChipError ret = dev->AttachToIndex(index);
    if (ret != CHIP_NO_ERROR) {
        mDevices[index] = nullptr;
        delete dev;
        return -1;
    }
    dev->SetDelegate(this);
    ChipLogProgress(DeviceLayer, "Added device %s to dynamic endpoint %d (index=%d)",
                    dev->GetName().c_str(), dev->GetEndpoint(), index);
    return 0;
}

int MatterBridge::DetachDevice(BridgedDevice *dev) {
    for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; index++) {
        if (mDevices[index] == dev) {
            DeviceLayer::StackLock lock;
            [[maybe_unused]] const EndpointId ep = emberAfClearDynamicEndpoint(index);
            mDevices[index] = nullptr;
            ChipLogProgress(DeviceLayer, "Removed device %s from dynamic endpoint %d (index=%d)",
                            dev->GetName().c_str(), ep, index);
            dev->SetDelegate(nullptr);
            return index;
        }
    }
    return -1;
}


int MatterBridge::AttachNodes() {
    for (const auto &node: mNodes) {
        ChipLogDetail(DeviceLayer, "node: %s", node->ToString().c_str());

        BridgedDevice *dev = nullptr;
        switch (node->GetDeviceType()) {
            case BridgedDevice::kDeviceTypeBridgedNode:
                dev = new BridgedDevice(node);
                break;             

            case LightOnOff::kDeviceTypeOnOffLight:
            case LightOnOff::kDeviceTypeOnOffSwitch:
                dev = new LightOnOff(node);
                break;

            case LightDimmer::kDeviceTypeDimmableLight:
                dev = new LightDimmer(node);
                break;

            case LightColor::kDeviceTypeColorLight:
                dev = new LightColor(node);
                break;

            case TemperatureSensor::kDeviceTypeTemperatureSensor:
                dev = new TemperatureSensor(node);
                break;

            case HumiditySensor::kDeviceTypeHumiditySensor:
                dev = new HumiditySensor(node);
                break;

            case Thermostat::kDeviceTypeThermostat:
                dev = new Thermostat(node);
                break;

            case OccupancySensor::kDeviceTypeOccupancySensor:
                dev = new OccupancySensor(node);
                break;

            case LightSensor::kDeviceTypeLightSensor:
                dev = new LightSensor(node);
                break;

            case Fan::kDeviceTypeFan:
                dev = new Fan(node);
                break;

            default:
                break;
        }

        if (dev == nullptr) {
            continue;
        }

        if (AttachDevice(dev) != 0) {
            delete dev;
        }

    }
    return 0;
}

uint16_t MatterBridge::GetNullptrIndex() const {
    for (uint16_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; index++) {
        if (mDevices[index] == nullptr) {
            return index;
        }
    }
    return CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
}

BridgedDevice *MatterBridge::GetDeviceByIndex(int index) const {
    if (index > CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) {
        return nullptr;
    }
    return mDevices[index];
}

BridgedDevice *MatterBridge::GetDeviceByEndpoint(EndpointId endpointId) const {
    for (auto node: mDevices) {
        if (node != nullptr && node->GetEndpoint() == endpointId) {
            return node;
        }
    }
    return nullptr;
}


using namespace chip;
using namespace chip::app::Clusters;

bool emberAfActionsClusterInstantActionCallback(app::CommandHandler *commandObj,
                                                const app::ConcreteCommandPath &commandPath,
                                                const Actions::Commands::InstantAction::DecodableType &commandData) {
    ChipLogProgress(DeviceLayer, "emberAfActionsClusterInstantActionCallback: ep=%d cluster:%08x command:%08x",
                    commandPath.mEndpointId,
                    commandPath.mClusterId,
                    commandPath.mCommandId);
    commandObj->AddStatus(commandPath, Protocols::InteractionModel::Status::NotFound);
    return true;
}


Protocols::InteractionModel::Status emberAfExternalAttributeReadCallback(EndpointId endpoint, ClusterId clusterId,
                                                                         const EmberAfAttributeMetadata *attributeMetadata,
                                                                         uint8_t *buffer,
                                                                         uint16_t maxReadLength) {
    const uint16_t index = emberAfGetDynamicIndexFromEndpoint(endpoint);
    ChipLogProgress(DeviceLayer, "emberAfExternalAttributeReadCallback: ep=%d cluster_id:%08x attribute:%08x", endpoint,
                    clusterId, attributeMetadata->attributeId);
    BridgedDevice *dev = BridgedMgr().GetDeviceByIndex(index);
    ChipLogProgress(DeviceLayer, "index=%d dev=%p", index, dev);
    if (dev == nullptr) {
        return Protocols::InteractionModel::Status::Failure;
    }
    return dev->ReadAttribute(clusterId, attributeMetadata->attributeId, buffer, maxReadLength);
}

Protocols::InteractionModel::Status emberAfExternalAttributeWriteCallback(EndpointId endpoint, ClusterId clusterId,
                                                                          const EmberAfAttributeMetadata *attributeMetadata,
                                                                          uint8_t *buffer) {
    const uint16_t index = emberAfGetDynamicIndexFromEndpoint(endpoint);
    ChipLogProgress(DeviceLayer, "AttributeWrite: ep=%d cluster:%x attribute:%x", endpoint, clusterId,
                    attributeMetadata->attributeId);

    BridgedDevice *dev = BridgedMgr().GetDeviceByIndex(index);
    if (dev == nullptr) {
        return Protocols::InteractionModel::Status::Failure;
    }
    return dev->WriteAttribute(clusterId, attributeMetadata, buffer);
}
